/*
    fracgen-c : Fractal Generator (using concurrency)
    Copyright (C) 2010 Arpit Sud, Sri Teja Basava & Sidartha Gracias

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

import util.Complex;
import fractal.Fractal;
import fractal.Mandelbrot;
import java.awt.image.BufferedImage;
import java.io.*;
import javax.imageio.*;

public class FractalGenerator 
{
	
	private static ComplexPlane cp;
	private static Fractal fractal;
	int numThreads = 1;
	private static int rowsCompleted = 0;
	private boolean isBW;
	
	public FractalGenerator()
	{
		cp = new ComplexPlane();
		fractal = new Mandelbrot();
		isBW = true;
	}
	
//return the complex plane member of the fractalgenerator class  
	public ComplexPlane getComplexPlane()
	{
		return cp;
	}
	
//setter function for the fractal member of fractal generator  
	public void setFractal(Fractal inputFractal)
	{
		fractal = inputFractal;
	}
	
//getter function for the fractal member of fractal generator  
	public Fractal getFractal()
	{
		return null;
	}
	
//setter function for the numthreads member of fractal generator  
	public void setNumThreads(int n)
	{
		numThreads = n;
	}

//getter function for the numthreads member of fractal generator  	
	public int getNumThreads()
	{
		return numThreads;
	}
	
//return percentage of fractal image completed 
// Represented as percentage of Row of total completed
	public int getPercentageComplete()
	{
		int percentComplete = (rowsCompleted*100/ComplexPlane.HEIGHT);
		return percentComplete;
	}

//method that called by each worker thread to update total number of rows completed
//Synchronized to ensure mutually exclusive access.
	public static synchronized void incrementCounter()
	{
		rowsCompleted++;
	}
	
//sets isBW = true, used to select Black and White image to generate 
	public void makeBW()
	{
		isBW = true;
	}

//sets isBW = false, used to select color image to generate  		
	public void makeColor()
	{
		isBW = false;
	}

//functions that performs fractal image generation, spawns multiple threads that perform computation 			
	public long generate()
	{
		rowsCompleted = 0;
		Thread[] t = new Thread[numThreads];
		int hEnd = 0, hStart = 0;
		int mod = ComplexPlane.HEIGHT/numThreads;
		long tStart, timeForCalc;
		tStart = System.currentTimeMillis();
//Split height into portion depending on number of worker threads 
		for(int i=0; i<numThreads; i++)
		{
			if(i == numThreads-1)
			{
				hEnd = ComplexPlane.HEIGHT;
			}
			else
			{
				hEnd += mod;
			}
			/*System.out.print(hStart);
			System.out.print(hEnd);
			System.out.print('\n');*/
			t[i] = new Thread(new generateMultiBW(i,hStart,hEnd,isBW));
			t[i].start();
			hStart = hEnd;
		}
		try 
		{
			for(int i=0; i<numThreads; i++)
			{
				t[i].join();
			}
		} catch (Exception ex) {
			      ex.printStackTrace();
		}
		timeForCalc = (System.currentTimeMillis() - tStart);
		
		/*
		Complex c;
		int pix;
		for(int j=0; j < ComplexPlane.HEIGHT; j++)
		{
			for(int i=0; i < ComplexPlane.WIDTH; i++)
			{
				c = cp.ptoc(i, j);
				pix = fractal.getBW(c);
				cp.getImage().setRGB(i, j, pix);
			}
		}
*/
		
		return timeForCalc;
	}

//Saves fractal image generated to file
	public void saveImage(String filePath)
	{
		try
		{
			BufferedImage bi = cp.getImage(); 
			File outputfile = new File(filePath);
			ImageIO.write(bi, "png", outputfile);
		} catch (IOException e)
		{
			
		}
	}

//Saves parameters used to generate fractal image in text format
	public void saveParameters(String filePath)
	{
		try {
		    BufferedWriter out = new BufferedWriter(new FileWriter(filePath));
		    out.write(fractal.name());
		    out.newLine();
		    for(int i= 0; i< fractal.parameters().length; i++)
		    {
		    	out.write(fractal.parameters()[i]);
			    out.newLine();		    
		    }
		    out.write("Real Axis: " + cp.getRealAxisScale());
		    out.newLine();
		    out.write("Imaginary Axis: " + cp.getImaginaryAxisScale());
		    out.newLine();
		    out.write("Center: " + cp.getCenter());
		    out.newLine();
		    out.close();
		} 
		catch (IOException e) 
		{
		
		}			
	}
	
// function to compute fractal image
// called by each thread to perform portion of computation depending on invoking thread	
	private static class generateMultiBW implements Runnable {
		int number,hS, hE;
		boolean isBW;
		public generateMultiBW(int name, int hStart, int hEnd, boolean isBW) {
			hS = hStart;
			hE = hEnd;
			number = name;
			this.isBW = isBW;
		}
		public void run() {
			Complex c;
			int pix;
			BufferedImage bi = cp.getImage();
			for(int j=hS; j < hE; j++)
			{
//				System.out.format("Thread: %d\n", number);
				for(int i=0; i < ComplexPlane.WIDTH; i++)
				{
					c = cp.ptoc(i, j);
					if (isBW)
						pix = fractal.getBW(c);
					else
						pix = fractal.getColor(c);
					bi.setRGB(i, j, pix);
				}
				incrementCounter();
			}
		}
	}
}